package funcs

import (
	"log"
	"net"
	"probeping/src/model"
	"probeping/src/store"
	"probeping/src/utils"
	"strconv"
	"sync"
	"time"

	_ "github.com/glebarez/go-sqlite"
)

var (
	statusLock sync.Mutex
)

func Ping() {
	var wg_intf sync.WaitGroup
	for _, inter := range store.Host.Interfaces {
		wg_intf.Add(1)
		go pingInterface(inter, &wg_intf)
	}
	wg_intf.Wait()
	// PrintStatus()
	UpdateNodes()
}

func pingInterface(intf model.Interface, wg_intf *sync.WaitGroup) {
	var wg_ip sync.WaitGroup
	for _, ip := range intf.IpRange {
		wg_ip.Add(1)
		go pingIp(intf.Ip.IP, ip, &wg_ip)
	}
	wg_ip.Wait()
	wg_intf.Done()
}

func PingOne(ip string) {
	ipNet := net.ParseIP(ip)
	var wg sync.WaitGroup
	wg.Add(1)
	pingIp(nil, ipNet, &wg)
	wg.Wait()
}

func pingIp(node net.IP, ip net.IP, wg *sync.WaitGroup) {
	stat := model.PingStatus{}
	stat.MinDelay = -1
	stat.StartTime = time.Now()
	lossPK := 0
	for i := 0; i < 4; i++ {
		starttime := time.Now().UnixNano()
		ipAddr := net.IPAddr{
			IP:   ip,
			Zone: "", // 可选的接口域（zone）
		}
		delay, err := utils.RunPing(&ipAddr, 3*time.Second, 64, i)
		if err == nil {
			stat.AvgDelay = stat.AvgDelay + delay
			if stat.MaxDelay < delay {
				stat.MaxDelay = delay
			}
			if stat.MinDelay == -1 || stat.MinDelay > delay {
				stat.MinDelay = delay
			}
			stat.RevcPk = stat.RevcPk + 1
			log.Println("[func:StartPing IcmpPing] ID:", i, " IP:", ip.String())
		} else {
			log.Println("[func:StartPing IcmpPing] ID:", i, " IP:", ip.String(), "| err:", err)
			lossPK = lossPK + 1
		}
		stat.SendPk = stat.SendPk + 1
		stat.LossPk = int((float64(lossPK) / float64(stat.SendPk)) * 100)
		duringtime := time.Now().UnixNano() - starttime
		time.Sleep(time.Duration(3000*1000000-duringtime) * time.Nanosecond)
	}
	stat.EndTime = time.Now()
	stat.Duration = stat.EndTime.Sub(stat.StartTime)
	log.Println(stat.StartTime, stat.EndTime, stat.Duration.Seconds())
	if stat.RevcPk > 0 {
		stat.AvgDelay = stat.AvgDelay / float64(stat.RevcPk)
	} else {
		stat.AvgDelay = 0.0
	}
	SaveStatus(node, ip.To4().String(), stat)
	PingStorage(ip.To4().String(), stat)
	wg.Done()
	log.Println("[func:IcmpPing] Finish Addr:", ip.String(), " MaxDelay:", stat.MaxDelay, " MinDelay:", stat.MinDelay, " AvgDelay:", stat.AvgDelay, " Revc:", stat.RevcPk, " LossPK:", stat.LossPk)
}

func SaveStatus(node net.IP, ip string, status model.PingStatus) {
	statusLock.Lock()
	for idx, intf := range store.Host.Interfaces {
		if intf.Ip.IP.Equal(node) {
			if store.Host.Interfaces[idx].PingStatus == nil {
				store.Host.Interfaces[idx].PingStatus = make(map[string]model.PingStatus, 0)
			}
			store.Host.Interfaces[idx].PingStatus[ip] = status
		}
	}
	statusLock.Unlock()
}

func PrintStatus() {
	log.Println("节点：", store.Host.Name)
	for _, intf := range store.Host.Interfaces {
		log.Println("接口：", intf.Ip.IP.To4().String())
		for k, v := range intf.PingStatus {
			if v.LossPk < 100 {
				log.Println("-->", k)
			}
		}
	}

}

func UpdateNodes() {
	if store.Host.Nodes == nil {
		store.Host.Nodes = make([]model.Host, 0)
	}
	for _, intf := range store.Host.Interfaces {
		for k, v := range intf.PingStatus {
			if v.LossPk < 100 {

				hostExist := false
				var host model.Host

				for _, fHost := range store.Host.Nodes {
					if fHost.Ip == k {
						host = fHost
						hostExist = true
						break
					}
				}

				if hostExist {
					host.Delay = v.AvgDelay
				} else {
					host = model.Host{}
					host.Ip = k

					names, err := net.LookupAddr(k)

					if err == nil && len(names) > 0 {
						host.Name = names[0]
					}
					host.Delay = v.AvgDelay
					store.Host.Nodes = append(store.Host.Nodes, host)
				}

			}
		}
	}

}

func PingStorage(ip string, pingStatus model.PingStatus) {
	logtime := time.Now().Format("2006-01-02 15:04")
	log.Println("[func:StartPing] ", "(", logtime, ")Starting PingStorage ", ip)
	sql := "INSERT INTO [pinglog] (logtime, target, maxdelay, mindelay, avgdelay, sendpk, revcpk, losspk) values('" + logtime + "','" + ip + "','" + strconv.FormatFloat(pingStatus.MaxDelay, 'f', 2, 64) + "','" + strconv.FormatFloat(pingStatus.MinDelay, 'f', 2, 64) + "','" + strconv.FormatFloat(pingStatus.AvgDelay, 'f', 2, 64) + "','" + strconv.Itoa(pingStatus.SendPk) + "','" + strconv.Itoa(pingStatus.RevcPk) + "','" + strconv.Itoa(pingStatus.LossPk) + "')"
	log.Println("[func:StartPing] ", sql)
	store.DLock.Lock()
	_, err := store.Db.Exec(sql)
	if err != nil {
		log.Println("[func:StartPing] Sql Error ", err)
	}
	store.DLock.Unlock()
	log.Println("[func:StartPing] ", "(", logtime, ") Finish PingStorage  ", ip)
}
